﻿/*
 * Almathy Center - ConnectionWindow.xaml.cs
 * Copyright (C) 2011 Christophe LEMOINE
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * */
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Threading;
using agsXMPP;
using agsXMPP.protocol.iq.disco;

namespace AlmathyCenter
{

    /// <summary>
    /// Logique d'interaction pour ConnectionWindow.xaml
    /// </summary>
    public partial class ConnectionWindow : Window
    {

        #region Errors manager

        private string ElementErrorConverter(int code)
        {
            switch (code)
            {
                case 0: return "Erreur générale.";
                case 200: return "Elément inconnu.";
                case 201: return "Jabber ID incorrect.";
                case 202: return "Demande non acceptée.";
                case 203: return "Opération non autorisée.";
                case 204: return "Finalisation requise.";
                case 205: return "Destinataire indsponible.";
                case 206: return "Enregistrement du compte requis.";
                case 207: return "Erreur distante.";
                case 208: return "Ressource valide requise.";
                case 209: return "Abonnement requis.";
                case 210: return "Condition inconnue.";
                case 302: return "Demande redirigé.";
                case 400: return "Demande incorrecte.";
                case 401: return "Compte non autaurisé.";
                case 402: return "Compte incorrect.";
                case 403: return "Compte refusé.";
                case 404: return "Demande inconnue.";
                case 405: return "Demande interdite.";
                case 406: return "Autorisation refusée.";
                case 407: return "Compte non enregistré.";
                case 408: return "Temps limite atteint.";
                case 409: return "Demande en conflit.";
                case 500: return "Erreur interne.";
                case 501: return "Demande non implémentée.";
                case 502: return "Erreur distante.";
                case 503: return "Service temporairement innaccessible.";
                case 504: return "Temps limite distant atteint.";
                case 510: return "Déconnecté.";
                default: return "Erreur inconnue.";
            }
        }

        private int ElementErrorConverter(agsXMPP.Xml.Dom.Element el)
        {
            int result = 0;
            if (el != null)
            {
                if (el.HasTag("error", true))
                {
                    agsXMPP.Xml.Dom.Element e = el.SelectSingleElement("error", true);
                    int code = 0;
                    if (e.HasAttribute("code")) { code = Convert.ToInt32(e.Attribute("code")); }
                    result = code;
                }
                else
                {
                    if (el.HasTag("bad-request", true)) { result = 400; }
                    else if (el.HasTag("conflict", true)) { result = 409; }
                    else if (el.HasTag("feature-not-implemented", true)) { result = 501; }
                    else if (el.HasTag("forbidden", true)) { result = 401; }
                    else if (el.HasTag("gone", true)) { result = 302; }
                    else if (el.HasTag("internal-server-error", true)) { result = 500; }
                    else if (el.HasTag("item-not-found", true)) { result = 200; }
                    else if (el.HasTag("jid-malformed", true)) { result = 201; }
                    else if (el.HasTag("not-acceptable", true)) { result = 202; }
                    else if (el.HasTag("not-allowed", true)) { result = 203; }
                    else if (el.HasTag("not-authorized", true)) { result = 406; }
                    else if (el.HasTag("payment-required", true)) { result = 204; }
                    else if (el.HasTag("recipient-unavailable", true)) { result = 205; }
                    else if (el.HasTag("redirect", true)) { result = 302; }
                    else if (el.HasTag("registration-required", true)) { result = 206; }
                    else if (el.HasTag("remote-server-not-found", true)) { result = 207; }
                    else if (el.HasTag("remote-server-timeout", true)) { result = 504; }
                    else if (el.HasTag("resource-constraint", true)) { result = 208; }
                    else if (el.HasTag("service-unavailable", true)) { result = 503; }
                    else if (el.HasTag("subscription-required", true)) { result = 209; }
                    else if (el.HasTag("undefined-condition", true)) { result = 210; }
                    else if (el.HasTag("unexpected-request", true)) { result = 400; }
                }
            }
            return result;
        }

        #endregion

        private XmppClientConnection xmpp = null;
        private Datas.AutoCompleteAccount CurrentAccount = null;
        private Datas.Vcard vCard = null;

        private Datas.AutoCompleteAccountsManager AccountsManager = new Datas.AutoCompleteAccountsManager();
        private bool addToFavorites = false;
        private bool updateFavorite = false;
        private bool ErrorHappened = false;
        private MainWindow mainWindow = null;

        public ConnectionWindow()
        {
            InitializeComponent();
            usernameBox.ItemsSource = AccountsManager.Accounts;
            usernameBox.Items.Refresh();
        }

        private void mainWindow_Closed(object sender, EventArgs e)
        {
            if (xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
                xmpp.Close();

            AccountsManager.Save();

            ErrorHappened = false;
            connectionButton.IsEnabled = true;
            this.IsEnabled = true;
            this.Show();
        }

        private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if(mainWindow != null)
                mainWindow.Close();

        }

        private void usernameBox_LostFocus(object sender, RoutedEventArgs e)
        {
            hostBox.Clear();
            serverBox.Clear();
            portBox.Text = "5222";
            checkCompression.IsChecked = false;
            checkSSL.IsChecked = false;
            if (usernameBox.Text.Trim() != string.Empty && !AccountsManager.ContainsUsername(usernameBox.Text))
            {
                expander1.IsExpanded = true;
            }
            else
            {
                Datas.AutoCompleteAccount aca = AccountsManager.GetAccountByUsername(usernameBox.Text);
                if (aca != null)
                {
                    hostBox.Text = aca.Host;
                    serverBox.Text = aca.Server;
                    portBox.Text = aca.Port.ToString();
                    checkCompression.IsChecked = aca.UseCompression;
                    checkSSL.IsChecked = aca.UseSSL;
                }
                expander1.IsExpanded = false;
            }
        }

        private void usernameBox_KeyUp(object sender, KeyEventArgs e)
        {
            statusMessage.Text = string.Empty;
            statusMessage.ToolTip = statusMessage.Text;
            connectionButton.IsEnabled = (((xmpp != null && xmpp.XmppConnectionState == XmppConnectionState.Disconnected) || xmpp == null) && usernameBox.Text.Trim() != string.Empty && hostBox.Text.Trim() != string.Empty && passwordBox.Password.Trim() != string.Empty);
            if (Keyboard.Modifiers == ModifierKeys.Shift && e.Key == Key.Delete)
            {
                Datas.AutoCompleteAccount aca = usernameBox.SelectedItem as Datas.AutoCompleteAccount;
                if (aca != null && AccountsManager.ContainsUsername(aca.Username) && Microsoft.Windows.Controls.MessageBox.Show("Êtes-vous certain de vouloir supprimer le favoris '" + aca.Username + "' (" + aca.Jid.Bare + ")?", "Favoris", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
                    AccountsManager.Remove(aca);

                hostBox.Clear();
                serverBox.Clear();
                portBox.Text = "5222";
                checkCompression.IsChecked = false;
                checkSSL.IsChecked = false;
                usernameBox.Text = string.Empty;
                usernameBox.Items.Refresh();
            }
        }

        private void passwordBox_KeyUp(object sender, KeyEventArgs e)
        {
            connectionButton.IsEnabled = (((xmpp != null && xmpp.XmppConnectionState == XmppConnectionState.Disconnected) || xmpp == null) && usernameBox.Text.Trim() != string.Empty && hostBox.Text.Trim() != string.Empty && passwordBox.Password.Trim() != string.Empty);
        }

        private void serverBox_KeyUp(object sender, KeyEventArgs e)
        {
            connectionButton.IsEnabled = (((xmpp != null && xmpp.XmppConnectionState == XmppConnectionState.Disconnected) || xmpp == null) && usernameBox.Text.Trim() != string.Empty && hostBox.Text.Trim() != string.Empty && passwordBox.Password.Trim() != string.Empty);
        }

        private void connectionButton_Click(object sender, RoutedEventArgs e)
        {
            connectionButton.IsEnabled = false;
            expander1.IsExpanded = false;
            ErrorHappened = false;
            int port;
            if (!Int32.TryParse(portBox.Text, out port))
            {
                port = (!(bool)checkSSL.IsChecked) ? 5222 : 5223;
                portBox.Text = port.ToString();
            }
            string host = hostBox.Text.Trim();
            string server = serverBox.Text.Trim();
            bool useCompression = (bool)checkCompression.IsChecked;
            string username = usernameBox.Text;
            bool useSSL = (bool)checkSSL.IsChecked;
            if (!AccountsManager.ContainsUsername(usernameBox.Text))
            {
                CurrentAccount = new Datas.AutoCompleteAccount();
                CurrentAccount.Host = host;
                CurrentAccount.Server = server;
                CurrentAccount.UseCompression = useCompression;
                CurrentAccount.Username = username;
                CurrentAccount.UseSSL = useSSL;
                addToFavorites = true;
                updateFavorite = false;
            }
            else
            {
                CurrentAccount = AccountsManager.GetAccountByUsername(usernameBox.Text);
                CurrentAccount.Host = host;
                CurrentAccount.Server = server;
                CurrentAccount.UseCompression = useCompression;
                CurrentAccount.Username = username;
                CurrentAccount.UseSSL = useSSL;
                addToFavorites = false;
                updateFavorite = true;
            }

            xmpp = new XmppClientConnection(agsXMPP.net.SocketConnectionType.Direct);
            xmpp.AutoAgents = false;
            xmpp.AutoPresence = false;
            xmpp.AutoResolveConnectServer = (CurrentAccount.Server == string.Empty);
            xmpp.AutoResolveConnectServer = true;
            xmpp.AutoRoster = false;
            xmpp.Capabilities.Node = "http://almathy.codeplex.com/Almathy/caps";
            xmpp.ClientVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
            xmpp.ConnectServer = CurrentAccount.Server;
            xmpp.DiscoInfo.AddIdentity(new DiscoIdentity("pc", Assembly.GetExecutingAssembly().GetName().Name, "client"));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_INFO));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_ITEMS));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.CAPS));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.IQ_TIME));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.IQ_VERSION));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.VCARD));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.PUBSUB));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.PUBSUB_EVENT));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.PUBSUB_OWNER));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature("http://jabber.org/protocol/mood"));
            xmpp.DiscoInfo.AddFeature(new DiscoFeature("http://jabber.org/protocol/activity"));
            xmpp.EnableCapabilities = true;
            xmpp.KeepAlive = true;
            xmpp.ClientLanguage = "fr";
            xmpp.Password = passwordBox.Password.Trim();
            xmpp.Port = CurrentAccount.Port;
            xmpp.Resource = Assembly.GetExecutingAssembly().GetName().Name;
            xmpp.Server = CurrentAccount.Host;
            xmpp.UseCompression = CurrentAccount.UseCompression;
            xmpp.Username = CurrentAccount.Username;
            xmpp.UseSSL = CurrentAccount.UseSSL;
            xmpp.UseStartTLS = false;

            xmpp.OnAuthError += new XmppElementHandler(xmppAuthError);
            xmpp.OnError += new ErrorHandler(xmppError);
            xmpp.OnRegisterError += new XmppElementHandler(xmppRegisterError);
            xmpp.OnSocketError += new ErrorHandler(xmppSocketError);
            xmpp.OnStreamError += new XmppElementHandler(xmppStreamError);
            xmpp.ClientSocket.OnValidateCertificate += new System.Net.Security.RemoteCertificateValidationCallback(xmppValidateCertificate);
            xmpp.OnSaslStart += new agsXMPP.sasl.SaslEventHandler(xmppSaslStart);
            xmpp.OnLogin += new ObjectHandler(xmppLogin);
            xmpp.OnRegistered += new ObjectHandler(xmppRegistered);
            xmpp.OnXmppConnectionStateChanged += new XmppConnectionStateHandler(xmppStateChanged);

            xmpp.Open();
        }

        private void usernameBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (usernameBox.SelectedItem != null)
                passwordBox.Clear();

            statusMessage.Text = string.Empty;
            statusMessage.ToolTip = statusMessage.Text;
        }

        private void checkSSL_Checked(object sender, RoutedEventArgs e)
        {
            portBox.Text = ((bool)checkSSL.IsChecked && portBox.Text == "5222") ? "5223" : portBox.Text;
        }

        private void checkSSL_Unchecked(object sender, RoutedEventArgs e)
        {
            portBox.Text = (!(bool)checkSSL.IsChecked && portBox.Text == "5223") ? "5222" : portBox.Text;
        }

        private void xmppAuthError(object sender, agsXMPP.Xml.Dom.Element element)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new XmppElementHandler(xmppAuthError), sender, element);
                return;
            }

            if (xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
                xmpp.Close();

            ErrorHappened = true;
            statusMessage.Text = "Erreur d'authentification! (" + ElementErrorConverter(ElementErrorConverter(element)) + ")";
            statusMessage.ToolTip = statusMessage.Text;
            connectionButton.IsEnabled = true;
        }

        private void xmppRegisterError(object sender, agsXMPP.Xml.Dom.Element element)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new XmppElementHandler(xmppRegisterError), sender, element);
                return;
            }

            if (xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
                xmpp.Close();

            ErrorHappened = true;
            statusMessage.Text = "Erreur d'enregistrement! (" + ElementErrorConverter(ElementErrorConverter(element)) + ")";
            statusMessage.ToolTip = statusMessage.Text;
            connectionButton.IsEnabled = true;
        }

        private void xmppStreamError(object sender, agsXMPP.Xml.Dom.Element element)
        { }

        private void xmppError(object sender, Exception e)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new ErrorHandler(xmppError), sender, e);
                return;
            }

            if (xmpp.XmppConnectionState != XmppConnectionState.Disconnected)
                xmpp.Close();

            CurrentAccount = null;
            connectionButton.IsEnabled = true;
            ErrorHappened = true;
            statusMessage.Text = "Erreur! (" + e.Message + ")";
            statusMessage.ToolTip = statusMessage.Text;
        }

        private void xmppSocketError(object sender, Exception e)
        { xmppError(sender, e); }

        private bool xmppValidateCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
        { return true; }

        private void xmppSaslStart(object sender, agsXMPP.sasl.SaslEventArgs e)
        { /*e.Mechanism = agsXMPP.protocol.sasl.Mechanism.GetMechanismName(agsXMPP.protocol.sasl.MechanismType.PLAIN);*/ }

        private void xmppLogin(object sender)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new ObjectHandler(xmppLogin), sender);
                return;
            }

            if (addToFavorites)
            {
                TaskDialogInterop.TaskDialogOptions tdo = new TaskDialogInterop.TaskDialogOptions();
                tdo.Owner = this;
                tdo.Title = "Favoris";
                tdo.MainInstruction = "Ajouter le compte '" + CurrentAccount.Username + "' à vos favoris?";
                tdo.Content = CurrentAccount.Jid.Bare;
                tdo.ExpandedInfo = "Pour supprimer un compte de vos favoris, il vous suffit de le sélectionner dans la liste, puis d'appuyer simultanement sur [SHIFT] + [SUPPR].";
                tdo.CustomButtons = new string[] { "&Enregister", "&Ignorer" };
                tdo.MainIcon = TaskDialogInterop.VistaTaskDialogIcon.Information;
                TaskDialogInterop.TaskDialogResult result = TaskDialogInterop.TaskDialog.Show(tdo);
                if(result.CustomButtonResult == 0)
                    AccountsManager.Add(CurrentAccount);

            }

            if (addToFavorites || updateFavorite)
                AccountsManager.Save();

            usernameBox.Items.Refresh();

            statusMessage.Text = "Téléchargement des données personnelles...";
            statusMessage.ToolTip = statusMessage.Text;

            vCard = new Datas.Vcard(xmpp);
            vCard.Retreived += new Datas.Vcard.RetreiveHandler(vCard_Retreived);
            vCard.RetreiveFrom(new agsXMPP.Jid(xmpp.MyJID.Bare));
            
        }

        private void vCard_Retreived(agsXMPP.Jid jid)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new Datas.Vcard.RetreiveHandler(vCard_Retreived), jid);
                return;
            }

            vCard.Retreived -= new Datas.Vcard.RetreiveHandler(vCard_Retreived);

            mainWindow = new MainWindow(xmpp, CurrentAccount, vCard);
            mainWindow.Closed += new EventHandler(mainWindow_Closed);

            this.Hide();
            mainWindow.Show();
        }

        private void xmppRegistered(object sender)
        {
        }

        private void usernameBox_TargetUpdated(object sender, DataTransferEventArgs e)
        {
            statusMessage.Text = string.Empty;
            statusMessage.ToolTip = statusMessage.Text;
        }

        private void xmppStateChanged(object sender, XmppConnectionState state)
        {
            if (!Dispatcher.CheckAccess())
            {
                Dispatcher.Invoke(DispatcherPriority.Send, new  XmppConnectionStateHandler(xmppStateChanged), sender, state);
                return;
            }

            this.IsEnabled = !(state == XmppConnectionState.SessionStarted);
            if (this.IsEnabled)
            {
                if(mainWindow != null)
                    mainWindow.Hide();

                this.Show();
            }

            connectionButton.IsEnabled = (state == XmppConnectionState.Disconnected && usernameBox.Text.Trim() != string.Empty && hostBox.Text.Trim() != string.Empty && passwordBox.Password.Trim() != string.Empty);
            if (ErrorHappened)
                connectionButton.IsEnabled = true;

            if (state == XmppConnectionState.Authenticated) { statusMessage.Text = "Authentifié."; }
            else if (state == XmppConnectionState.Authenticating) { statusMessage.Text = "Authentification en cours..."; }
            else if (state == XmppConnectionState.Binded) { statusMessage.Text = "Serveur atteint."; }
            else if (state == XmppConnectionState.Binding) { statusMessage.Text = "Atteinte du serveur en cours..."; }
            else if (state == XmppConnectionState.Compressed) { statusMessage.Text = "Transmission compressée."; }
            else if (state == XmppConnectionState.Connected) { statusMessage.Text = "Connecté au serveur."; }
            else if (state == XmppConnectionState.Connecting) { statusMessage.Text = "Connexion au serveur en cours..."; }
            else if (state == XmppConnectionState.Disconnected && !ErrorHappened) { statusMessage.Text = "Déconnecté."; }
            else if (state == XmppConnectionState.Registered) { statusMessage.Text = "Compte enregistré."; }
            else if (state == XmppConnectionState.Registering) { statusMessage.Text = "Enregistrement du compte..."; }
            else if (state == XmppConnectionState.Securing) { statusMessage.Text = "Sécurisation de la transmission en cours..."; }
            else if (state == XmppConnectionState.SessionStarted) { statusMessage.Text = "Session démarrée. Enjoy."; }
            else if (state == XmppConnectionState.StartCompression) { statusMessage.Text = "Compression de la transmission en cours..."; }
            else if (state == XmppConnectionState.StartSession) { statusMessage.Text = "Démarrage de la session en cours..."; }
            else if (!ErrorHappened) { statusMessage.Text = string.Empty; }
            statusMessage.ToolTip = statusMessage.Text;

        }

    }

}
